package com.xmlit.project.engine.xsd;

import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.events.Namespace;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;

import com.xmlit.project.engine.Utils;
import com.xmlit.project.engine.struct.Struct;
import com.xmlit.project.engine.struct.StructChoice;
import com.xmlit.project.engine.struct.StructSequence;
import com.xmlit.project.engine.struct.StructSimple;
import com.xmlit.project.engine.struct.impl.CalcContext;
import com.xmlit.project.engine.struct.impl.StructImpl;
import com.xmlit.project.engine.struct.impl.StructSequenceImpl;
import com.xmlit.project.engine.struct.impl.StructSimpleImpl;

public class Struct2XSD {
	public static final String xsdNamespace = "http://www.w3.org/2001/XMLSchema";
	static DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
	static DocumentBuilder db = null;// dbf.newDocumentBuilder();
	static {
		try {
			db = dbf.newDocumentBuilder();
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public static String struct2XSD(Struct struct) {
		Document doc = db.newDocument();
		Element root = doc.createElementNS(xsdNamespace, "schema");
		doc.appendChild(root);
		// root.setAttribute("xmlns", "http://www.w3.org/2001/XMLSchema");
		root.setAttribute("targetNamespace", "http://www.xml-it.com/xsd");
		root.setAttribute("xmlns:tns", "http://www.xml-it.com/xsd");
		root.setAttribute("elementFormDefault", "qualified");
		doStruct2XSD(root, doc, struct);

		return Utils.dom2String(doc);
	}

	private static void doStruct2XSD(Element root, Document doc, Struct struct) {
		Element current = doc.createElementNS(xsdNamespace, "element");
		current.setAttribute("name", struct.getName());
		String delimiter = null;
		String lookahead = null;
		boolean not = false;
		String prefix = struct.getPrefix();
		String suffix = struct.getSuffix();
		String allowedChars = null;
		int maxLength = -1;
		int minLength = -1;

		if (struct instanceof StructSequence) {
			delimiter = ((StructSequence) struct).getDelimiter();
			lookahead = ((StructSequence) struct).getLookahead();
			not = ((StructSequence) struct).isNegative();
		} else if (struct instanceof StructSimple) {
			allowedChars = ((StructSimple) struct).getAllowedChars();
			maxLength = ((StructSimple) struct).getMaxLength();
			minLength = ((StructSimple) struct).getMinLength();

		}
		handleAnnotation(doc, current, delimiter, prefix, suffix, lookahead,
				not, allowedChars, maxLength, minLength);

		if (1 != struct.getMinOccurrences())
			current.setAttribute("minOccurs", "" + struct.getMinOccurrences());
		if (1 != struct.getMaxOccurrences())
			current.setAttribute("maxOccurs", "" + struct.getMaxOccurrences());

		root.appendChild(current);
		if (struct instanceof StructSimple) {
			String regex = ((StructSimple) struct).getSimplePattern();
			String allowedVals = ((StructSimple) struct).getAllowedValues();

			Element restriction = (Element) current.appendChild(
					doc.createElementNS(xsdNamespace, "simpleType"))
					.appendChild(
							doc.createElementNS(xsdNamespace, "restriction"));
			restriction.setAttribute("base", "string");
			if (regex != null) {
				Element pattern = (Element) restriction.appendChild(doc
						.createElementNS(xsdNamespace, "pattern"));
				pattern.setAttribute("value", regex);
			} else if (allowedVals != null) {
				for (String val : allowedVals.split("\n")) {
					Element pattern = (Element) restriction.appendChild(doc
							.createElementNS(xsdNamespace, "enumeration"));
					pattern.setAttribute("value", val.trim());
				}

			}
		} else {
			String type = (struct instanceof StructChoice) ? "choice"
					: "sequence";
			Element typeElement = (Element) current.appendChild(
					doc.createElementNS(xsdNamespace, "complexType"))
					.appendChild(doc.createElementNS(xsdNamespace, type));
			if (struct.getChildren() != null)
				for (Struct child : struct.getChildren()) {
					doStruct2XSD(typeElement, doc, child);
				}
			else {
				Element any = doc.createElementNS(xsdNamespace, "any");
				any.setAttribute("minOccurs", "0");
				typeElement.appendChild(any);

			}
		}

	}

	private static void handleAnnotation(Document doc, Element current,
			String delimiter, String prefix, String suffix, String lookahead,
			boolean not, String allowedChars, int maxLength, int minLength) {
		if (delimiter != null || prefix != null || suffix != null
				|| lookahead != null || allowedChars != null) {
			Element xmlitdata = (Element) current
					.appendChild(
							doc.createElementNS(xsdNamespace, "annotation"))
					.appendChild(doc.createElementNS(xsdNamespace, "appinfo"))
					.appendChild(doc.createElement("tns:xmlitdata"));
			if (delimiter != null)
				xmlitdata.setAttribute("delimiter", delimiter);
			if (prefix != null)
				xmlitdata.setAttribute("prefix", prefix);
			if (suffix != null)
				xmlitdata.setAttribute("suffix", suffix);
			if (lookahead != null)
				xmlitdata.setAttribute("lookahead", lookahead);
			if (not)
				xmlitdata.setAttribute("not", "" + not);
			if (allowedChars != null)
				xmlitdata.setAttribute("allowedChars", allowedChars);
			if (maxLength != -1)
				xmlitdata.setAttribute("maxLength", maxLength + "");
			if (minLength != -1)
				xmlitdata.setAttribute("minLength", minLength + "");

		}
	}

	public static void main(String[] args) throws SAXException,
			ParserConfigurationException, Exception {
		StructSequenceImpl root = new StructSequenceImpl("root");
		root.addChild(((StructSimple) new StructSimpleImpl("NewElement"))
				.setSimplePattern("/"));

		System.out.println(Struct2XSD.struct2XSD(XSD2Struct
				.xsd2Struct(Struct2XSD.struct2XSD(root))));

	}

}
